package com.xinsoft.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinsoft.common.exception.BDException;
import com.xinsoft.constant.Const;
import com.xinsoft.common.CountBean;
import com.xinsoft.entity.dto.InOrOutStock;
import com.xinsoft.entity.dto.ScmSaleOutstockDTO;
import com.xinsoft.entity.dto.ScmSaleOutstockParam;
import com.xinsoft.entity.po.*;
import com.xinsoft.entity.vo.ScmSaleListVo;
import com.xinsoft.entity.vo.ScmSaleNoticeListVo;
import com.xinsoft.entity.vo.ScmSaleOutstockListVo;
import com.xinsoft.entity.vo.ScmSaleOutstockVo;
import com.xinsoft.mapper.ScmSaleOutstockItemMapper;
import com.xinsoft.mapper.ScmSaleReturnMapper;
import com.xinsoft.service.*;
import com.xinsoft.mapper.ScmSaleOutstockMapper;
import com.xinsoft.utils.TokenUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ZYM
 * @description 针对表【scm_sale_outstock(销售出库)】的数据库操作Service实现
 * @createDate 2023-07-20 15:20:16
 */
@Service
public class ScmSaleOutstockServiceImpl extends ServiceImpl<ScmSaleOutstockMapper, ScmSaleOutstock>
        implements ScmSaleOutstockService {
    @Resource
    private ScmSaleOutstockMapper saleOutstockMapper;
    @Resource
    private ScmSaleOutstockItemMapper saleOutstockItemMapper;
    @Resource
    private ScmSaleReturnMapper saleReturnMapper;

    @Autowired
    private ScmStockService stockService;
    @Autowired
    private SysDocumentNumService sysDocumentNumService;
    @Autowired
    private ScmSaleOutstockItemService saleOutstockItemService;
    @Autowired
    private ScmDocumentService documentService;
    @Autowired
    private SysConfigService configService;
    @Autowired
    private SysMaterialService materialService;
    @Autowired
    private ScmSaleService saleService;
    @Autowired
    private ScmSaleNoticeService saleNoticeService;


    @Override
    public IPage<ScmSaleOutstockListVo> findPageListByParam(ScmSaleOutstockParam param) {
        Page<ScmSaleOutstockListVo> page = new Page<>(param.getCurrentPage(), param.getPageSize());
        IPage<ScmSaleOutstockListVo> pageList = saleOutstockMapper.findPageListByParam(page, param);

        // 被引用时计算可用数量
        if (pageList.getRecords().size() > 0 && Objects.equals(param.getCiteType(), 0)) {
            List<ScmSaleOutstockListVo> voList = this.calculateAvailableQuantity(pageList.getRecords());
            List<ScmSaleOutstockListVo> collect = voList.stream().filter(obj -> obj.getPrimaryUnitCount().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
            pageList.setRecords(CollectionUtil.isNotEmpty(collect) ? collect : null);
            pageList.setTotal(CollectionUtil.isNotEmpty(collect) ? collect.size() : 0);
        }
        return pageList;
    }

    @Override
    public List<ScmSaleOutstockListVo> findListByParam(ScmSaleOutstockParam param) {
        // IPage的参数 size<0 ，临时不分页
        Page<ScmSaleOutstockListVo> page = new Page<>(param.getCurrentPage(), -1);
        IPage<ScmSaleOutstockListVo> pageListByParam = saleOutstockMapper.findPageListByParam(page, param);
        return pageListByParam.getRecords();
    }

    /**
     * 计算可用数量
     */
    public List<ScmSaleOutstockListVo> calculateAvailableQuantity(List<ScmSaleOutstockListVo> list) {
        if (list.size() > 0) {
            // 计算被销售退货引用的数量
            List<Integer> sourceIds = list.stream().map(ScmSaleOutstockListVo::getId).distinct().collect(Collectors.toList());
            List<CountBean> countBeanList = saleReturnMapper.getUsedCountOfSaleReturn(sourceIds, Const.ApproveType.XSCK);
            Map<String, CountBean> usedCountMap = countBeanList.stream().collect(Collectors.toMap(CountBean::getId, o -> o));
            list.forEach(item -> {
                CountBean countBean = usedCountMap.get(String.join("_", item.getId().toString(), item.getMaterialId().toString()));
                if (countBean != null) {
                    item.setPrimaryUnitCount(item.getOutstockNum().subtract(countBean.getPrimaryUnitUsedCount()));
                    item.setAuxiliaryUnitCount(item.getAssistOutstockNum().subtract(countBean.getAuxiliaryUnitUsedCount()));
                } else {
                    item.setPrimaryUnitCount(item.getOutstockNum());
                    item.setAuxiliaryUnitCount(item.getAssistOutstockNum());
                }
            });
        }
        return list;
    }

    @Override
    public List<ScmSaleOutstockListVo> findList(List<Integer> idList, List<Integer> materialIdList) {
        ScmSaleOutstockParam param = new ScmSaleOutstockParam();
        param.setIdList(idList);
        param.setMaterialIdList(materialIdList);
        List<ScmSaleOutstockListVo> listByParam = saleOutstockMapper.findPageListByParam(param);
        return this.calculateAvailableQuantity(listByParam);
    }

    @Override
    public Integer tempSave(ScmSaleOutstockDTO scmSaleOutstockDTO) {
        Integer defaultBill = Integer.valueOf(configService.getOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getParamKey, "SCM_SALE_OUTSTOCK_DEFAULT_BILL")).getParamValue());
        ScmSaleOutstock saleOutstock = new ScmSaleOutstock();
        BeanUtils.copyProperties(scmSaleOutstockDTO, saleOutstock);
        saleOutstock.setAuditStatus(Const.AUDIT_STATUS_WAIT);
        saleOutstock.setDoClose(Const.CloseStatus.NOT_CLOSED);
        List<ScmSaleOutstockItem> outstockItemList = scmSaleOutstockDTO.getScmSaleOutstockItemList();
        this.checkNum(outstockItemList, defaultBill);
        // 保存或新增
        if (Objects.isNull(saleOutstock.getId())) {
            // 更新计划单号
            sysDocumentNumService.incrementLastSerialByDocumentId(Const.DOCUMENT_ID.SALE_OUTSTOCK_DOCUMENT);
            this.save(saleOutstock);
        } else {
            documentService.remove(Wrappers.lambdaQuery(ScmDocument.class).eq(ScmDocument::getScmId, saleOutstock.getId()));
            saleOutstockItemService.remove(Wrappers.lambdaQuery(ScmSaleOutstockItem.class).eq(ScmSaleOutstockItem::getSaleOutstockId, saleOutstock.getId()));
            this.updateById(saleOutstock);
        }

        // 保存销售出库明细
        outstockItemList.forEach(item -> {
            item.setSaleOutstockId(saleOutstock.getId());
            if (defaultBill.equals(1)) {
                item.setSourceType(Const.ApproveType.XSDD);
            } else if (defaultBill.equals(0)) {
                item.setSourceType(Const.ApproveType.FHTZ);
            }
        });
        saleOutstockItemService.saveBatch(outstockItemList);
        // 保存附件
        List<SysFileHis> fileHisList = scmSaleOutstockDTO.getSysFileHisList();
        documentService.saveDocument(fileHisList, saleOutstock.getId(), Const.ApproveType.XSCK);
        return saleOutstock.getId();
    }

    /**
     * 校验数量
     *
     * @param itemList    生产领用明细
     * @param defaultBill 默认关联单据
     */
    public void checkNum(List<ScmSaleOutstockItem> itemList, Integer defaultBill) {
        long count = itemList.stream().filter(item -> Objects.isNull(item.getWarehouseId()) || Objects.isNull(item.getWarehItemId()) || Objects.isNull(item.getBatch())).count();
        if (count > 0) {
            throw new BDException("请查询库存");
        }
        List<String> failMsgList = new ArrayList<>();
        List<Integer> sourceIds = itemList.stream().map(ScmSaleOutstockItem::getSourceId).distinct().collect(Collectors.toList());
        List<Integer> materialIds = itemList.stream().map(ScmSaleOutstockItem::getMaterialId).distinct().collect(Collectors.toList());
        List<SysMaterial> sysMaterials = materialService.listByIds(materialIds);
        Map<Integer, String> materialMap = sysMaterials.stream().collect(Collectors.toMap(SysMaterial::getId, SysMaterial::getMaterialName));
        // 校验可执行数量
        if (defaultBill.equals(1)) { // 关联销售订单
            List<ScmSaleListVo> list = saleService.findList(sourceIds, materialIds, 1);
            Map<String, ScmSaleListVo> voMap = list.stream().collect(Collectors.toMap(vo -> String.join("_", vo.getId().toString(), vo.getMaterialId().toString()), vo -> vo));
            itemList.forEach(item -> {
                String key = String.join("_", item.getSourceId().toString(), item.getMaterialId().toString());
                if (voMap.containsKey(key) && item.getOutstockNum().compareTo(voMap.get(key).getPrimaryUnitCount()) > 0) {
                    failMsgList.add(String.format("物料[%s]批次为[%s]的源单可用数量[%s]", materialMap.get(item.getMaterialId()), item.getBatch(), voMap.get(key).getPrimaryUnitCount()));
                    // failMsgList.add(String.format("物料[%s]批次为[%s]的出库数量不能大于源单可用数量", materialMap.get(item.getMaterialId()), item.getBatch()));
                }
            });
        }
        if (defaultBill.equals(0)) { // 关联发货通知
            List<ScmSaleNoticeListVo> list = saleNoticeService.findList(sourceIds, materialIds);
            Map<String, ScmSaleNoticeListVo> voMap = list.stream().collect(Collectors.toMap(vo -> String.join("_", vo.getId().toString(), vo.getMaterialId().toString()), vo -> vo));
            itemList.forEach(item -> {
                String key = String.join("_", item.getSourceId().toString(), item.getMaterialId().toString());
                if (voMap.containsKey(key) && item.getOutstockNum().compareTo(voMap.get(key).getPrimaryUnitCount()) > 0) {
                    failMsgList.add(String.format("物料[%s]批次为[%s]的源单可用数量[%s]", materialMap.get(item.getMaterialId()), item.getBatch(), voMap.get(key).getPrimaryUnitCount()));
                    // failMsgList.add(String.format("物料[%s]批次为[%s]的出库数量不能大于源单可用数量", materialMap.get(item.getMaterialId()), item.getBatch()));
                }
            });
        }
        if (CollectionUtil.isNotEmpty(failMsgList)) {
            failMsgList.add("出库数量不能大于源单可用数量");
            throw new BDException(String.join(",\n", failMsgList));
        }
        // 校验库存数量
        itemList.forEach(item -> {
            ScmStock stock = stockService.getOne(Wrappers.lambdaQuery(ScmStock.class)
                    .eq(ScmStock::getMaterialId, item.getMaterialId())
                    .eq(ScmStock::getWarehouseId, item.getWarehouseId())
                    .eq(ScmStock::getWarehItemId, item.getWarehItemId())
                    .eq(ScmStock::getBatchNo, item.getBatch()));
            if (stock.getBatchNumFree().compareTo(item.getOutstockNum()) < 0) {
                failMsgList.add(String.format("物料[%s]批次为[%s]的出库数量不能大于库存可用数量", materialMap.get(item.getMaterialId()), item.getBatch()));
            }
        });
        if (CollectionUtil.isNotEmpty(failMsgList)) {
            throw new BDException(String.join(",\n", failMsgList));
        }
    }

    @Override
    public ScmSaleOutstockVo detail(Integer id) {
        ScmSaleOutstockVo saleOutstockVo = saleOutstockMapper.getOutStockById(id);
        // 销售出库明细
        saleOutstockVo.setScmSaleOutstockItemList(saleOutstockItemMapper.listByOutstockId(id));
        // 附件列表
        saleOutstockVo.setSysFileHisList(documentService.getFileHisListByScmId(id, Const.ApproveType.XSCK));
        return saleOutstockVo;
    }

    @Override
    public void del(List<Integer> ids) {
        List<ScmSaleOutstock> list = this.list(Wrappers.lambdaQuery(ScmSaleOutstock.class)
                .eq(ScmSaleOutstock::getAuditStatus, Const.AUDIT_STATUS_NORMAL)
                .in(ScmSaleOutstock::getId, ids));
        if (list.size() > 0) {
            throw new BDException("操作失败，只能删除暂存的销售出库单");
        }
        saleOutstockItemService.remove(Wrappers.lambdaQuery(ScmSaleOutstockItem.class).in(ScmSaleOutstockItem::getSaleOutstockId, ids));
        documentService.remove(Wrappers.lambdaQuery(ScmDocument.class).in(ScmDocument::getScmId, ids));
        this.removeByIds(ids);
    }

    @Override
    public void audit(Integer id) {
        ScmSaleOutstock outstock = this.getById(id);
        if (Const.AUDIT_STATUS_NORMAL.equals(outstock.getAuditStatus())) {
            throw new BDException("该单据已审核，无需重复审核");
        }
        List<ScmSaleOutstockItem> itemList = saleOutstockItemService.list(Wrappers.lambdaQuery(ScmSaleOutstockItem.class)
                .eq(ScmSaleOutstockItem::getSaleOutstockId, id));
        Integer defaultBill = Integer.valueOf(configService.getOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getParamKey, "SCM_SALE_OUTSTOCK_DEFAULT_BILL")).getParamValue());
        this.checkNum(itemList, defaultBill);
        this.update(Wrappers.lambdaUpdate(ScmSaleOutstock.class)
                .eq(ScmSaleOutstock::getId, id)
                .set(ScmSaleOutstock::getAuditStatus, Const.AUDIT_STATUS_NORMAL)
                .set(ScmSaleOutstock::getAuditId, TokenUtil.getUserId())
                .set(ScmSaleOutstock::getAuditTime, new Date()));
        // 更新库存
        List<InOrOutStock> inOrOutStockList = this.getInOrOutStockList(id);
        stockService.updateInventory(Const.SourceBillType.XSCK, outstock.getSaleOutstockNo(), inOrOutStockList, Const.ChangeType.SUBTRACT);
    }

    /**
     * 获取出入库明细列表
     * @param id
     * @return {@link List}<{@link InOrOutStock}>
     */
    public List<InOrOutStock> getInOrOutStockList(Integer id) {
        List<InOrOutStock> list = new ArrayList<>();
        List<ScmSaleOutstockItem> items = saleOutstockItemService.list(Wrappers.lambdaQuery(ScmSaleOutstockItem.class).eq(ScmSaleOutstockItem::getSaleOutstockId, id));
        items.forEach(item -> {
            InOrOutStock inOrOutStock = new InOrOutStock();
            BeanUtils.copyProperties(item,inOrOutStock);
            inOrOutStock.setQty(item.getOutstockNum());
            list.add(inOrOutStock);
        });
        return list;
    }

    @Override
    public void reverseAudit(Integer id) {
        ScmSaleOutstock outstock = this.getById(id);
        if (Const.AUDIT_STATUS_WAIT.equals(outstock.getAuditStatus())) {
            throw new BDException("该单据未审核，无需反审核");
        }
        int citeCount = saleOutstockMapper.getSaleReturnCiteCount(id);
        if (citeCount > 0) {
            throw new BDException("该单据已产生下游单据，不能反审核");
        }
        outstock.setAuditId(null);
        outstock.setAuditTime(null);
        outstock.setAuditStatus(Const.AUDIT_STATUS_WAIT);
        this.updateById(outstock);
        // 更新库存
        List<InOrOutStock> inOrOutStockList = this.getInOrOutStockList(id);
        stockService.updateInventory(Const.SourceBillType.XSCK, outstock.getSaleOutstockNo(), inOrOutStockList, Const.ChangeType.ADD);
    }
}




